package ch.exm.storm.functional;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ch.exm.storm.introspection.IntrospectionUtil;

public class DynamicWrapper implements InvocationHandler {

	private Object wrapped;
	private Object modifier;
	
	private Map<String, List<Method>> modifierMethodsByName;
	
	public DynamicWrapper(Object wrapped, Object modifier) {
		this.wrapped = wrapped;
		this.modifier = modifier;
		this.modifierMethodsByName = parseModifierMethods();
	}
	
	public Object getWrapper() {
		return Proxy.newProxyInstance(wrapped.getClass().getClassLoader(), wrapped.getClass().getInterfaces(), this);
	}
	
	private Map<String, List<Method>> parseModifierMethods() {
		Map<String, List<Method>> methodMap = new HashMap<String, List<Method>>();
		for (Method m : modifier.getClass().getMethods()) {
			String name = m.getName();
			List<Method> ms = methodMap.get(name);
			if (ms == null) {
				ms = new ArrayList<Method>();
				methodMap.put(name, ms);
			}
			ms.add(m);
		}
		return methodMap;
	}
	
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		String methodName = method.getName();
		List<Method> methods = modifierMethodsByName.get(methodName);
		Method modifierMethod = methods != null ? IntrospectionUtil.getMethod(methods, modifier.getClass(), methodName, args) : null;
		return modifierMethod != null ? modifierMethod.invoke(modifier, args) : method.invoke(wrapped, args);
	}

}
